GTK_TREE_VIEW_UNSET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
}
+static inline gboolean
+row_is_separator (GtkTreeView *tree_view,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ gboolean is_separator = FALSE;
+
+ if (tree_view->priv->row_separator_func)
+ {
+ GtkTreeIter tmpiter;
+
+ if (iter)
+ tmpiter = *iter;
+ else
+ gtk_tree_model_get_iter (tree_view->priv->model, &tmpiter, path);
+
+ is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
+ &tmpiter,
+ tree_view->priv->row_separator_data);
+ }
+
+ return is_separator;
+}
+
static gboolean
gtk_tree_view_button_press (GtkWidget *widget,
GdkEventButton *event)
GdkRectangle cell_area;
gint vertical_separator;
gint horizontal_separator;
+ gboolean path_is_selectable;
gboolean rtl;
g_return_val_if_fail (GTK_IS_TREE_VIEW (widget), FALSE);
/* Get the path and the node */
path = _gtk_tree_view_find_path (tree_view, tree, node);
+ path_is_selectable = !row_is_separator (tree_view, NULL, path);
+
+ if (!path_is_selectable)
+ {
+ gtk_tree_path_free (path);
+ grab_focus_and_unset_draw_keyfocus (tree_view);
+ return TRUE;
+ }
+
depth = gtk_tree_path_get_depth (path);
background_area.y = y_offset + event->y;
background_area.height = ROW_HEIGHT (tree_view, GTK_RBNODE_GET_HEIGHT (node));
do
{
+ /* Small optimization by assuming insensitive nodes are never
+ * selected.
+ */
+ if (!GTK_RBNODE_FLAG_SET (start_node, GTK_RBNODE_IS_SELECTED))
+ {
+ GtkTreePath *path;
+ gboolean selectable;
+
+ path = _gtk_tree_view_find_path (tree_view, start_tree, start_node);
+ selectable = _gtk_tree_selection_row_is_selectable (tree_view->priv->selection, start_node, path);
+ gtk_tree_path_free (path);
+
+ if (!selectable)
+ goto node_not_selectable;
+ }
+
if (select)
{
if (tree_view->priv->rubber_band_shift)
_gtk_tree_view_queue_draw_node (tree_view, start_tree, start_node, NULL);
+node_not_selectable:
if (start_node == end_node)
break;
gboolean is_first = FALSE;
gboolean is_last = FALSE;
- if (tree_view->priv->row_separator_func)
- {
- is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
- &iter,
- tree_view->priv->row_separator_data);
- }
+ is_separator = row_is_separator (tree_view, &iter, NULL);
max_height = ROW_HEIGHT (tree_view, BACKGROUND_HEIGHT (node));
! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))
return FALSE;
- if (tree_view->priv->row_separator_func)
- {
- is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
- iter,
- tree_view->priv->row_separator_data);
- }
+ is_separator = row_is_separator (tree_view, iter, NULL);
gtk_widget_style_get (GTK_WIDGET (tree_view),
"focus-padding", &focus_pad,
return (focus_child != NULL);
}
+/* This function returns in 'path' the first focusable path, if the given path
+ * is already focusable, it's the returned one.
+ */
+static gboolean
+search_first_focusable_path (GtkTreeView *tree_view,
+ GtkTreePath **path,
+ gboolean search_forward,
+ GtkRBTree **new_tree,
+ GtkRBNode **new_node)
+{
+ GtkRBTree *tree = NULL;
+ GtkRBNode *node = NULL;
+
+ if (!path || !*path)
+ return FALSE;
+
+ _gtk_tree_view_find_node (tree_view, *path, &tree, &node);
+
+ if (!tree || !node)
+ return FALSE;
+
+ while (node && row_is_separator (tree_view, NULL, *path))
+ {
+ if (search_forward)
+ _gtk_rbtree_next_full (tree, node, &tree, &node);
+ else
+ _gtk_rbtree_prev_full (tree, node, &tree, &node);
+
+ if (*path)
+ gtk_tree_path_free (*path);
+
+ if (node)
+ *path = _gtk_tree_view_find_path (tree_view, tree, node);
+ else
+ *path = NULL;
+ }
+
+ if (new_tree)
+ *new_tree = tree;
+
+ if (new_node)
+ *new_node = node;
+
+ return (*path != NULL);
+}
+
static gint
gtk_tree_view_focus (GtkWidget *widget,
GtkDirectionType direction)
(! GTK_WIDGET_REALIZED (tree_view)))
return;
- GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
-
cursor_path = NULL;
if (tree_view->priv->cursor)
cursor_path = gtk_tree_row_reference_get_path (tree_view->priv->cursor);
if (cursor_path == NULL)
{
- /* Consult the selection before defaulting to the first element */
+ /* Consult the selection before defaulting to the
+ * first focusable element
+ */
+ GList *selected_rows;
+ GtkTreeModel *model;
GtkTreeSelection *selection;
- GtkTreeModel *model;
- GList *selected_rows;
selection = gtk_tree_view_get_selection (tree_view);
selected_rows = gtk_tree_selection_get_selected_rows (selection, &model);
+
if (selected_rows)
{
cursor_path = gtk_tree_path_copy((const GtkTreePath *)(selected_rows->data));
g_list_free (selected_rows);
}
else
- cursor_path = gtk_tree_path_new_first ();
+ {
+ cursor_path = gtk_tree_path_new_first ();
+ search_first_focusable_path (tree_view, &cursor_path,
+ TRUE, NULL, NULL);
+ }
gtk_tree_row_reference_free (tree_view->priv->cursor);
tree_view->priv->cursor = NULL;
- if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE)
- gtk_tree_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
- else
- gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
+ if (cursor_path)
+ {
+ if (tree_view->priv->selection->type == GTK_SELECTION_MULTIPLE)
+ gtk_tree_view_real_set_cursor (tree_view, cursor_path, FALSE, FALSE);
+ else
+ gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
+ }
}
- gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
- gtk_tree_path_free (cursor_path);
- if (tree_view->priv->focus_column == NULL)
+ if (cursor_path)
{
- GList *list;
- for (list = tree_view->priv->columns; list; list = list->next)
+ GTK_TREE_VIEW_SET_FLAG (tree_view, GTK_TREE_VIEW_DRAW_KEYFOCUS);
+
+ gtk_tree_view_queue_draw_path (tree_view, cursor_path, NULL);
+ gtk_tree_path_free (cursor_path);
+
+ if (tree_view->priv->focus_column == NULL)
{
- if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
+ GList *list;
+ for (list = tree_view->priv->columns; list; list = list->next)
{
- tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
- break;
+ if (GTK_TREE_VIEW_COLUMN (list->data)->visible)
+ {
+ tree_view->priv->focus_column = GTK_TREE_VIEW_COLUMN (list->data);
+ break;
+ }
}
}
}
&new_cursor_tree, &new_cursor_node);
}
+ if (new_cursor_node)
+ {
+ cursor_path = _gtk_tree_view_find_path (tree_view,
+ new_cursor_tree, new_cursor_node);
+
+ search_first_focusable_path (tree_view, &cursor_path,
+ (count != -1),
+ &new_cursor_tree,
+ &new_cursor_node);
+
+ if (cursor_path)
+ gtk_tree_path_free (cursor_path);
+ }
+
/*
* If the list has only one item and multi-selection is set then select
* the row (if not yet selected).
GtkRBNode *cursor_node = NULL;
GtkTreePath *old_cursor_path = NULL;
GtkTreePath *cursor_path = NULL;
+ GtkRBTree *start_cursor_tree = NULL;
+ GtkRBNode *start_cursor_node = NULL;
gint y;
gint window_y;
gint vertical_separator;
y -= tree_view->priv->cursor_offset;
cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
- g_return_if_fail (cursor_path != NULL);
+
+ start_cursor_tree = cursor_tree;
+ start_cursor_node = cursor_node;
+
+ if (! search_first_focusable_path (tree_view, &cursor_path,
+ (count != -1),
+ &cursor_tree, &cursor_node))
+ {
+ /* It looks like we reached the end of the view without finding
+ * a focusable row. We will step backwards to find the last
+ * focusable row.
+ */
+ cursor_tree = start_cursor_tree;
+ cursor_node = start_cursor_node;
+ cursor_path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+
+ search_first_focusable_path (tree_view, &cursor_path,
+ (count == -1),
+ &cursor_tree, &cursor_node);
+ }
+
+ if (!cursor_path)
+ goto cleanup;
+
+ /* update y */
+ y = _gtk_rbtree_node_find_offset (cursor_tree, cursor_node);
+
gtk_tree_view_real_set_cursor (tree_view, cursor_path, TRUE, FALSE);
y -= window_y;
if (!gtk_tree_path_compare (old_cursor_path, cursor_path))
gtk_widget_error_bell (GTK_WIDGET (tree_view));
+cleanup:
gtk_tree_path_free (old_cursor_path);
gtk_tree_path_free (cursor_path);
}
{
while (cursor_node && cursor_node->left != cursor_tree->nil)
cursor_node = cursor_node->left;
+
+ /* Now go forward to find the first focusable row. */
+ path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+ search_first_focusable_path (tree_view, &path,
+ TRUE, &cursor_tree, &cursor_node);
}
else
{
cursor_node = cursor_tree->root;
}
while (1);
+
+ /* Now go backwards to find last focusable row. */
+ path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+ search_first_focusable_path (tree_view, &path,
+ FALSE, &cursor_tree, &cursor_node);
}
- path = _gtk_tree_view_find_path (tree_view, cursor_tree, cursor_node);
+ if (!path)
+ goto cleanup;
if (gtk_tree_path_compare (old_path, path))
{
gtk_widget_error_bell (GTK_WIDGET (tree_view));
}
+cleanup:
gtk_tree_path_free (old_path);
gtk_tree_path_free (path);
}
}
gtk_tree_row_reference_free (tree_view->priv->cursor);
+ tree_view->priv->cursor = NULL;
+
+ /* One cannot set the cursor on a separator. */
+ if (!row_is_separator (tree_view, NULL, path))
+ {
+ tree_view->priv->cursor =
+ gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
+ tree_view->priv->model,
+ path);
+ _gtk_tree_view_find_node (tree_view, path, &tree, &node);
+ }
- tree_view->priv->cursor = gtk_tree_row_reference_new_proxy (G_OBJECT (tree_view),
- tree_view->priv->model,
- path);
- _gtk_tree_view_find_node (tree_view, path, &tree, &node);
if (tree != NULL)
{
GtkRBTree *new_tree = NULL;
path))
return NULL;
- if (tree_view->priv->row_separator_func)
- {
- is_separator = (* tree_view->priv->row_separator_func) (tree_view->priv->model,
- &iter,
- tree_view->priv->row_separator_data);
- }
+ is_separator = row_is_separator (tree_view, &iter, NULL);
cell_offset = x;